--
--    queue multiple Websites Retrieval HTTP Get Retrieve requests
--    run each in turn, using another thread
--

package body WGetter is

   --
   -- synchronized type for storage of website download requests and statuses
   --

   protected body Requests_Queue_Type
   is

      --
      -- request download URL, save to file
      --

      entry Add_Req( Website_URL : in String;

                     File_Name   : in String )

        when Requests_Queue_Type.Queue'Size < Max_Download_Requests

      is

         Website_In   : constant String := Website_URL;

         File_Name_In : constant String := File_Name;

      begin

         Info_Message( "Add_Req: " & Website_URL );

         if( Contains( Container => Requests_Queue.Queue,
                       Key => Website_In ) )
         then

            Error_Message( "Ignoring duplicate request. URL already being"
                           & " downloaded: " & Website_In );
            return;

         end if;

         Insert( Container => Requests_Queue.Queue,
                 Key => Website_In,
                 New_Item => File_Name_In );

      end Add_Req;


      --
      -- mark download URL as complete, removing from requests queue
      --

      entry Remove_Req( Website_URL : in String )

         when not Is_Empty( Requests_Queue_Type.Queue )

      is

         Website_In : constant String := Website_URL;

      begin

         if( not Contains( Container => Requests_Queue.Queue,
                           Key => Website_In ) )
         then

            Error_Message( "Cannot remove request. URL not found in queue: "
                           & Website_In );
            return;

         end if;

         Delete( Container => Requests_Queue.Queue,
                 Key => Website_In );

      end Remove_Req;


      --
      -- update / replace-into a download URL's % complete
      --

      entry Update_Status( Website_URL : in String;

                           Percentage_Complete : in Percentage_Type_Int )

         when Requests_Queue_Type.Statuses'Size < Max_Download_Requests

      is

         Website_In    : constant String := Website_URL;

         Percentage_In : constant Percentage_Type_Int := Percentage_Complete;

      begin

         if( not Contains( Container => Requests_Queue.Statuses,
                           Key => Website_In ) )
         then

            Insert( Container => Requests_Queue.Statuses,
                    Key => Website_In,
                    New_Item => Percentage_In );

         else

            Replace( Container => Requests_Queue.Statuses,
                     Key => Website_In,
                     New_Item => Percentage_In );

         end if;


      end Update_Status;


      --
      -- retrieve download URL % complete
      --

      entry Get_Status( Website_URL         : in String;
                        Percentage_Complete : out Percentage_Type_Int )

         when not Is_Empty( Requests_Queue.Statuses )

      is

         Website_In : constant String := Website_URL;

      begin

         if( not Contains( Container => Requests_Queue.Statuses,
                           Key => Website_In ) )
         then

            Error_Message( "URL status not available: " & Website_In );

         else

            Percentage_Complete := Element(

                  Container => Requests_Queue.Statuses,
                  Key       => Website_In );

            if( not Contains( Container => Requests_Queue.Queue,
                               Key => Website_In ) )
            then

               Error_Message( "Also, URL was not found in queue: "
                              & Website_In );

            end if;

         end if;

         Percentage_Complete := 0;


      end Get_Status;


      --
      -- retrieve first entry: URL, file name
      --
      --    provides a synchronized iteration, FIFO access
      --

      entry Fetch_Req( Website_URL_Fetched : out String;

                       File_Name_Fetched   : out String )

         when not Is_Empty( Requests_Queue_Type.Queue )

      is

         Requests_Cursor : constant Requests_Map_Type.Cursor :=
               Requests_Queue_Type.Queue
                                  .First;

      begin

         Website_URL_Fetched := Key( Position => Requests_Cursor );

         File_Name_Fetched   := Element( Position => Requests_Cursor );

         Remove_Req( Website_URL_Fetched );


      end Fetch_Req;


      --
      -- encapsulation routines for immutable access to the private
      --


      --
      -- immutable copy of download requests
      --

      entry Copy_Queue( Map_In : out Requests_Map_Type.Map )

         when not Is_Empty( Requests_Queue.Queue )

      is


      begin

         for Requests_Cursor in Requests_Queue.Queue
                                              .Iterate loop
            declare

               URL : constant Website_Address_Arg_Type := Requests_Map_Type

                     .Key( Requests_Cursor ) ;

               File_Name : constant Website_Address_Arg_Type :=

                     Requests_Map_Type.Element( Requests_Cursor ) ;

            begin

               Insert( Container => Map_In,
                       Key       => URL,
                       New_Item  => File_Name );

            end;

         end loop;


      end Copy_Queue;


      --
      -- immutable copy of % complete for the download requests
      --

      entry Copy_Statuses( Map_In : out Requests_Status_Map_Type.Map )

         when not Is_Empty( Requests_Queue.Statuses )

      is

      begin

         for Requests_C in Requests_Queue.Statuses
                                              .Iterate loop
            declare

               URL : constant Website_Address_Arg_Type :=

                     Requests_Status_Map_Type.Key( Requests_C ) ;

               Percentage_Complete : constant Percentage_Type_Int :=

                     Requests_Status_Map_Type.Element( Requests_C ) ;

            begin

               Insert( Container => Map_In,
                       Key       => URL,
                       New_Item  => Percentage_Complete );

            end;

         end loop;


      end Copy_Statuses;

   end Requests_Queue_Type;


   --
   -- download a URL to a file
   --
   --    adds request to queue, starts/restarts processing loop for requests
   --

   procedure WGet( Website : String;

                   File_Name : File_Name_Arg_Type )

   is

      Website_In   : constant String := Website;

      File_Name_In : constant String := File_Name;

      Active_Object : Active_Object_Processing;

   begin

      Requests_Queue.Add_Req( Website_URL => Website_In,
                              File_Name   => File_Name_In );

      --
      -- start active object processing loop
      --

      Active_Object.Start;

   end WGet;



   --
   -- starts/restarts download processing for queued requests
   --
   --    active object processing loop / watching of queue
   --

   task body Active_Object_Processing

   is
   begin

      loop  -- infinite / until all download requests have been processed

         declare

            task type Download_URL_Task is

               entry Start_Request( Website_URL_For_Task : in String;
                                    File_Name_For_Task   : in String );

            end Download_URL_Task;


            task body Download_URL_Task is

            begin

               select

                  accept Start_Request( Website_URL_For_Task : in String;
                                        File_Name_For_Task   : in String )
                  do

                     Info_Message( "Download_URL_Task starting: "
                                   & Website_URL_For_Task );

                     WebGet_Obj.Retrieve(

                           Host_Address_Str => Website_URL_For_Task,

                           File_Name_Str => File_Name_For_Task );

                  end;

               or

                  terminate;

               end select;

            end Download_URL_Task;

            Download_URL : Download_URL_Task;

            Website_URL_From_Queue : String ( 1 .. HTTP_REQUEST_MAX_LENGTH );
            File_Name_From_Queue   : String ( 1 .. FILE_NAME_MAX_LENGTH );

         begin

            --
            -- prevent execution until triggered in procedural logic
            --

            select

               accept Start;

            or

               terminate;

            end select;


            Info_Message("Active object processing started");

            --
            -- process queue of requests until empty
            --

            select

               --
               -- obtain URL, file name from queue of requests
               --
               accept Fetch_Req( Website_URL_Fetched : out String;

                          File_Name_Fetched : out String )

               do

                  Website_URL_From_Queue := Website_URL_Fetched;

                  File_Name_From_Queue := File_Name_Fetched;


                  Download_URL.Start_Request(

                        Website_URL_For_Task => Website_URL_From_Queue,

                        File_Name_For_Task   => File_Name_From_Queue );

               end Fetch_Req;

            or

               terminate;

            end select;

         end;

      end loop;


   end Active_Object_Processing;


end WGetter;